import json
import redis
from Inversion import Inversion
inver = Inversion()
pool = redis.ConnectionPool(
                host='119.29.26.75',
                port=20004,
                db=0,
                password='App123456',
                # decode_responses=True
)
rs = redis.Redis(connection_pool=pool)
pipe = rs.pipeline(transaction=True)

pipe.get('station_id')
# 车站id
station_id = json.loads(str(pipe.execute()[-1], encoding='utf-8'))

print(station_id)

pipe.get('station_yard')

# 站场表示信息格式清单
station_yard = json.loads(str(pipe.execute()[-1], encoding='utf-8'))

pipe.get('train')
train = json.loads(str(pipe.execute()[-1], encoding='utf-8'))

pipe.get('plan')
# 计划信息映射
plan = json.loads(str(pipe.execute()[-1], encoding='utf-8'))

pipe.get('first_last_train')

# 首末班信息映射
first_last_train = json.loads(str(pipe.execute()[-1], encoding='utf-8'))
pipe.get('Door_isolation_status')
Door_isolation_status = json.loads(str(pipe.execute()[-1], encoding='utf-8'))
pipe.get('Platform')
Platform = json.loads(str(pipe.execute()[-1], encoding='utf-8'))


pipe.get('equipment_type')

# 设备色码清单
equipment_type = json.loads(str(pipe.execute()[-1], encoding='utf-8'))

# 将低位在前的16进制转化为高位在前，并转化成10进制
def inversion(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    # print('data', data)
    if isinstance(data, bytes):
        data = str(data, encoding='utf-8')
    # 切割
    val = data.split('x')[-1]
    val_list = list()
    for i in range(0,len(val),2):
        val_list.append(val[i:i+2:1])
    new_val_list = val_list[::-1]
    new_data = ''.join(new_val_list)
    new_data = '0x' + new_data
    return int(new_data, 16)


# 将低位在前变为高位在前
def reversal(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    if isinstance(data,bytes):
        data=str(data, encoding='utf-8')
    # 切割
    val=data.split('x')[-1]
    val_list=list()
    for i in range(0, len(val), 2):
        val_list.append(val[i:i + 2:1])
    new_val_list=val_list[::-1]
    new_data=''.join(new_val_list)
    new_data='0x' + new_data
    return new_data


class Unpack_Content(object):


    # 解析站台门隔离状态信息
    def unpack_platform_door(slef, content):
        global Platform
        # 切割所在位置
        position = 0
        # 接受之后的数据存放在改字典中
        un_platform_door = dict()
        # 站台个数
        # key = list(Platform.keys())[0]
        # 站台个数所占字节长度
        platform_num_len = Platform.get('Platform_Number')
        # print('len', platform_num_len)
        platform_num = content[position:position + platform_num_len * 2 + 2:1]
        # print(platform_num)
        # 站台个数
        platform_num = int(platform_num, 16)
        un_platform_door['Platform_Number'] = platform_num
        position += platform_num_len * 2 + 2
        # 切割出需要重复循环解析的内容
        content = content[position::]
        # print(content)
        # 每个循环内容的长度
        platform_content_len = int(len(content) / platform_num)
        # print(platform_content_len)
        position = 0
        un_platform_door['Platform_Info'] = list()
        # del Platform['站台个数']
        # 站台个数已经解析完成，所以要排除在外
        keys = list(Platform.keys())[1:]
        # 循环制全部内容解析完成
        while position < len(content):
            # 每一条循环的信息都存放在一个字典中
            NID_PSD = dict()
            # 每一条循环的信息切片的起始位置
            one_position = 0
            # 每次循环切片出一条用于循环解析的内容
            one_content = content[position:position + platform_content_len:1]

            position += platform_content_len
            for Platform_key in keys:
                value = Platform[Platform_key]
                # 如果值为int类型，说明value为该信息片段所占的字节到校
                if isinstance(value, int):

                    platform_id = one_content[one_position:one_position + value * 2 + 2:1]
                    # 站台（股道）ID
                    platform_id = int(platform_id, 16)

                    # un_platform_door['站台门信息'].append()
                    NID_PSD[Platform_key] = platform_id
                    one_position += value * 2 + 2
                    # position += value*2+2
                # 如果value是字典，遍历字典继续解析内容
                else:
                    NID_PSD[Platform_key] = dict()
                    for key, val in value.items():
                        if key == 'length':
                            id = one_content[one_position:one_position + val * 2 + 2:1]
                            # 1或2侧站台门id
                            id = int(id, 16)
                            # position += val*2+2
                            one_position += val * 2 + 2
                            # 如果id为0，表示通信位置不存在站台门，跳过本次循环
                            if id == 0:
                                continue
                            NID_PSD[Platform_key]['id'] = id
                        else:
                            NID_PSD[Platform_key][key] = dict()
                            cont = one_content[one_position:one_position + val * 2 + 2:1]
                            # 每个bit代表一个门，按照每一个bit控制，例如bit 0表示该侧站台1号门，bit 15表示该侧站台16号门，以此类推
                            cont = bin(int(cont, 16))[2:]
                            # 不足8个字节高位补0
                            if len(cont) < val * 8:
                                cont = '0' * (val * 8 - len(cont)) + cont
                            cont = cont[::-1]
                            # 对每个bit解析
                            for i in range(len(cont)):
                                NID_PSD[Platform_key][key][f'站台门{i + 1}'] = cont[i]
                            # position += val*2+2
                            one_position += val * 2 + 2
            un_platform_door['Platform_Info'].append(NID_PSD)
        # 返回解析后的内容
        return un_platform_door


    # 解析车门隔离状态信息
    def unpack_door_isolation_status(slef,content):
        # 切片位置
        position = 0
        # 解析后的数据存放在这个字典之中
        un_door_isolation_status = dict()
        # unpack_door_isolation_status['故障隔离站台门要求'] = list()
        # 循环站台门隔离状态信息的映射字典
        for key, value in Door_isolation_status.items():
            # 如果为int类型，说明value为该信息片段所占字节的大小
            if isinstance(value, int):
                length = value
                # 切片出站台(股道)ID的信息片段
                platform_id = content[position:position + length * 2 + 2:1]
                # 站台(股道)ID
                platform_id = int(platform_id, 16)
                un_door_isolation_status[key] = platform_id
                # 记录已经解析到的位置
                position += length * 2 + 2
            # 如果是字典，需要进步拆分解析
            else:
                # unpack_door_isolation_status['故障隔离站台门要求']
                # NID_PSD = dict()
                # 1或2侧站台门ID所占字节大小
                length = value.get('length')
                # 1或2侧站台门ID的信息片段
                NID_PSD_id = content[position:position + length * 2 + 2:1]
                # 1或2侧站台门ID
                NID_PSD_id = int(NID_PSD_id, 16)
                if NID_PSD_id == 0:
                    continue
                # print('nid',NID_PSD_id)
                un_door_isolation_status[key] = dict()
                un_door_isolation_status[key]['id'] = NID_PSD_id
                position += length * 2 + 2
                un_door_isolation_status[key]['Quarantine_Demand'] = list()
                status_len = value.get('Quarantine_Demand')
                # 故障隔离站台门要求信息片段
                status = content[position:position + status_len * 2 + 2:1]
                # NID_PSD['']
                # print('st', status)
                # 故障隔离站台门要求2进制
                status = bin(int(status, 16))[2:]
                # 一个bit位代表一个门，按照每一个bit控制，例如bit 0表示该侧站台1号门,不足16位的高位补0
                if len(status) < status_len * 8:
                    status = '0' * (status_len * 2 - len(status)) + status
                status = status[::-1]
                for i in range(len(status)):
                    un_door_isolation_status[key]['Quarantine_Demand'].append({
                        f'站台门{i + 1}': status[i]
                    })

                position += status_len * 2 + 2
        # 返回解析后的信息
        return un_door_isolation_status
        # print('s',status)

    # 用于解析列车信息的方法
    def unpack_train(self,content):
        global train
        # 解析数据切割到的位置
        position = 0
        # loop_position = 0
        # 解析后的数据存放在该字典中
        un_train = dict()
        #
        # train_len = len(content)
        # 列车信息映射的字典的所有key值列表
        keys = list(train.keys())
        # 表示列车个数的信息所占的字节大小
        train_num_len = train.get(keys[0])
        # 切片出对应的信息
        train_num = content[position:position + train_num_len * 2 + 2:1]
        # 转化成10进制数
        train_num = inversion(train_num)
        un_train[keys[0]] = train_num
        # print(train_num)
        # 每次记录解析到的位置
        position += train_num_len * 2 + 2
        # 列车信息映射的所有key值列表
        loop_keys = keys[1::]
        # 每一列车信息都存放在一个list中
        un_train['Train_Info'] = list()
        # 循环解析每一列车的信息
        for i in range(train_num):

            train_cont = dict()
            # 根据索引遍历列车信息映射的所有key值列表
            for j in range(len(loop_keys)):
                # 改信息片段所占字节大小
                length = train.get(loop_keys[j])
                # print(length)
                # 如果所占字节大小为"n",该信息片段所占字节大小有上一个信息片段定义
                if length == 'n':
                    # train_cont = un_train.get('列车信息')
                    length = train_cont.get(loop_keys[j - 1])
                    # cont =
                # 切片出对应的信息
                cont = content[position:position + length * 2 + 2:1]
                cont = inversion(cont)
                train_cont[loop_keys[j]] = cont
                position += length * 2 + 2
            un_train['Train_Info'].append(train_cont)
        # 返回解析后的所有内容
        return un_train

    # 解析首末班车信息
    def unpack_first_last_train(self, content):
        global first_last_train
        # 每次记录解析到的位置
        position = 0
        # 解析后的完整内容存放在这个字典之中
        un_first_last_train = dict()
        # "Platform_Number"所占字节大小
        platform_num_len = first_last_train.get('Platform_Number')
        # 切片出对应的信息
        platform_num = content[position:position + platform_num_len * 2 + 2:1]
        # 调用转化成10进制的方法
        platform_num = inversion(platform_num)
        un_first_last_train['Platform_Number'] = platform_num
        position += platform_num_len * 2 + 2
        # 首末班车信息的映射字典的key列表
        platform_keys = list(first_last_train.keys())
        # Platform_Number已经解析过，所以移除
        platform_keys.remove('Platform_Number')
        # 每一个首末班车信息，都存放在一个列表之中
        un_first_last_train['Train_Info'] = list()
        # 解析每一个站台的首末班车信息
        for i in range(platform_num):
            # 每一个首末列表信息就是一个字典
            un_first_last_train['Train_Info'].append(dict())
            for k_i in range(len(platform_keys)):
                # first_last_train_cont = dict()
                # 获得key
                first_last_train_key = platform_keys[k_i]
                # 每一个信息片段所占的字节大小
                length = first_last_train.get(first_last_train_key)
                # 如果是一个字典，说明是日期信息，该信息片段包含多个日期信息，如果不是则可以直接进行解析
                if not isinstance(length, dict):
                    # 如果是"n",说明该信息片段所占字节大小由上一个信息片段定义的
                    if length == 'n':
                        # 获得这个信息片段所占字节大小
                        length = un_first_last_train.get('Train_Info')[i][platform_keys[k_i - 1]]
                    # 切片出对应的信息内容
                    cont = content[position:position + length * 2 + 2:1]
                    cont = inversion(cont)
                    # 存放到字典之中
                    un_first_last_train['Train_Info'][i][first_last_train_key] = cont
                    # un_first_last_train[first_last_train_key]=cont
                    position += length * 2 + 2
                else:
                    date = first_last_train.get(first_last_train_key)  # type:dict
                    # 将年月日时分秒解析出来后整合成完整的日期信息
                    datetime = ''
                    for key, length in date.items():
                        # print(key)
                        # 切割出对应的内容
                        value = content[position:position + length * 2 + 2:1]
                        # print(value)
                        # 整合成完整的日期信息
                        datetime += str(inversion(value)) + key
                        position += length * 2 + 2
                    un_first_last_train['Train_Info'][i][first_last_train_key] = datetime
                # un_first_last_train['Train_Info'].append(first_last_train_cont)
        # 返回完整的解析内容
        return un_first_last_train

    # 用于解析计划信息的方法
    def unpack_plan(self, content):
        global plan
        # 解析到的位置
        position = 0
        # 解析出来的内容放在这个字典里
        un_plan = dict()
        # Platform_Number所占字节数
        platform_num_len = plan.get('Platform_Number').get('length')
        platform_num = content[position:position + platform_num_len * 2 + 2:1]
        # Platform_Number
        platform_num = inversion(platform_num)
        un_plan['Platform_Number'] = platform_num
        # 解析到的位置要随之变化
        position += platform_num_len * 2 + 2
        # 每个站台的计划信息都存放在一个list中
        un_plan['Platform_Info'] = list()
        # 一个站台的计划信息的映射信息的全部的键的列表
        platform_keys = list(plan.get('Platform_Number').keys())
        # length 表示"Platform_Number所占字节"，因为已经解析完毕所以移除
        platform_keys.remove('length')
        #  一个站台的计划信息的映射信息
        platform = plan.get('Platform_Number')
        # 解析所有站台的计划信息
        for i in range(platform_num):
            # 一个站台的计划信息存放一个字典
            un_plan['Platform_Info'].append(dict())
            for platform_key in platform_keys:
                # if platform_key == 'length':
                #     continue
                # 如果该信息片段对应的映射不是一个字典，说明可以直接解析
                if not isinstance(platform.get(platform_key), dict):
                    # 该信息片段所占的字节
                    length = platform.get(platform_key)
                    # 根据所占的字节大小切割出对应内容
                    value = content[position:position + length * 2 + 2:1]
                    # print('value',value)
                    # 16进制转化为10进制
                    value = inversion(value)
                    un_plan['Platform_Info'][i][platform_key] = value
                    position += length * 2 + 2
                #  如果该信息片段对应的映射是一个字典，说明这个信息片段代表的是"站台列车趟数",由这个信息可知下一个循环的次数
                else:
                    # 信息片段的所占字节大小
                    length = platform.get(platform_key).get('length')
                    # 切割出对应字段
                    value = content[position:position + length * 2 + 2:1]
                    # 该信息片段的对应值(站台列车趟数)
                    train_num = inversion(value)
                    un_plan['Platform_Info'][i][platform_key] = train_num

                    # train_num = value
                    # 每次记录解析到的位置
                    position += length * 2 + 2
                    # 每一趟列车的计划信息统一存放在一个list中
                    un_plan['Platform_Info'][i]['Train_Info'] = list()
                    # 列车计划信息的映射字典
                    train = platform.get(platform_key)  # type:dict
                    # 列车计划信息的映射字典全部key值存放在一个list中
                    train_keys = list(platform.get(platform_key).keys())
                    # 移除length,length表示的是"站台列车趟数"所占字节大小，已经解析完毕所以移除
                    train_keys.remove('length')
                    # 解析每趟列车的计划信息
                    for j in range(train_num):
                        # 每趟列车的计划信息单独存放一个字典
                        un_plan['Platform_Info'][i]['Train_Info'].append(dict())
                        # 按索引遍历列车计划信息的映射字典的key值列表
                        for k_i in range(len(train_keys)):
                            # 获得key
                            train_key = train_keys[k_i]
                            # 如果该信息片段对应的映射是一个字典，说明改信息片段包含了多个信息(例如:"计划到站时间"包含了年月日等多个信息)
                            # 如果不是该信息片段可直接解析
                            if not isinstance(train.get(train_key), dict):
                                # 该信息片段所占字节大小
                                length = train.get(train_key)
                                if length == 'n':
                                    # 如果该信息片段所占字节大小为'n'表示改信息片段所占字节大小由上个信息片段定义
                                    length = un_plan['Platform_Info'][i]['Train_Info'][j][train_keys[k_i - 1]]
                                # print(length)
                                # 依据信息片段所占字节大小切片
                                value = content[position:position + length * 2 + 2:1]
                                value = inversion(value)
                                un_plan['Platform_Info'][i]['Train_Info'][j][train_key] = value
                                #
                                position += length * 2 + 2
                            else:
                                # 都为日期信息
                                date = train.get(train_key)  # type:dict
                                # 将年月日时分秒解析出来后整合成完整的日期信息
                                datetime = ''
                                for key, length in date.items():
                                    # print(key)
                                    # 切割出对应的内容
                                    value = content[position:position + length * 2 + 2:1]
                                    # print(value)
                                    # 整合成完整的日期信息
                                    datetime += str(inversion(value)) + key
                                    position += length * 2 + 2

                                un_plan['Platform_Info'][i]['Train_Info'][j][train_key] = datetime
        # 返回完整的解析内容
        return un_plan

    # 解析战场表示信息
    def unpack_station_yard_content(self, content):
        global station_yard, equipment_type
        unpack_content = dict()
        # 用于记录解析到的位置
        position = 0
        loop_content = ''
        # 循环内容解析到的位置
        loop_position = 0
        # 取得战场表示信息的key值
        keys = list(station_yard.keys())
        RTU_key = keys[0]
        # RTU16进制长度
        RTU_len = station_yard.get(RTU_key) * 2 + 2
        # RTU 16进制
        RTU = content[position:position + RTU_len:1]
        # RTU状态
        unpack_content[RTU_key] = inversion(RTU)
        # 记录当前的切割位置
        position += RTU_len
        station_num_len = station_yard.get(keys[1]) * 2 + 2
        station_num = content[position:position + station_num_len:1]
        # 获得车站信息个数
        station_num = inversion(station_num)
        unpack_content[keys[1]] = station_num
        # 记录切割到的位置
        position += station_num_len
        # if key == '车站信息个数':
        loop_content = content[position::1]
        # print(unpack_content.get('车站信息个数'))
        # 遍历车站信息
        station_info_keys = keys[2:]
        for i in range(station_num):
            station_code = loop_content[loop_position:loop_position + station_yard.get('Station_Code') * 2 + 2:1]
            code_len = station_yard.get('Station_Code') * 2 + 2
            # 车站编码
            station_code = inversion(station_code)
            # print('station_code',station_code)
            # 获得车站名称
            station_name = station_id.get(station_code)
            # print(station_name)
            # print(station_code,station_name)
            unpack_content[station_name] = {'station_code': station_code}
            position += code_len
            loop_position += code_len
            # 所谓的站场表示信息长其实就是用于表示设备信息的长度
            station_equipment_len = loop_content[
                                    loop_position:loop_position + station_yard.get('Station_Yard_Length') * 2 + 2:1]
            station_equipment_len = inversion(station_equipment_len)
            unpack_content[station_name]['Station_Yard_Length'] = station_equipment_len
            position += station_yard.get('Station_Yard_Length') * 2 + 2
            loop_position += station_yard.get('Station_Yard_Length') * 2 + 2
            #
            station_position = 0
            # 取得设备相关的映射key列表
            equipment_keys = keys[4:]
            # 解析出每一个设备
            while station_position < station_equipment_len:
                color_code = ''
                for equipment_key_index in range(len(equipment_keys)):
                    equipment_key = equipment_keys[equipment_key_index]
                    # 每一个片段信息的长度
                    length = station_yard.get(equipment_key)

                    if length != 'n':
                        cont = loop_content[loop_position:loop_position+length*2+2:1]
                        if equipment_key != 'Equipment_Type':
                            cont = inversion(cont)
                            unpack_content[equipment_key] = cont
                        else:
                            # 如果是色码，不用转化成10进制数
                            Type = loop_content[loop_position:loop_position+length*2+2:1]
                            # print(Type)
                    # 如果长度是n,说明该信息片段由上一个信息片段定义
                    else:
                        length = unpack_content[equipment_keys[equipment_key_index-1]]
                        # print(length)
                        # 得到色码
                        color_code = loop_content[loop_position:loop_position+length*2+2:1]
                        # length = inversion(length)
                        # color_code = loop_content[loop_position:loop_position+length*2+2:1]
                    loop_position += length *2 + 2
                    position += length *2 + 2
                    station_position += length
                # 将色码反向切割成一个列表
                color_code = color_code[::-1]
                # 根据设备类型获得设备的映射
                Equipment_mapping = equipment_type.get(Type)
                # 设备名称
                equipment_name = Equipment_mapping.get('name')
                # 获得设备名称
                unpack_content['equipment_name'] = equipment_name
                # 获取每一位2进制位所表示的设备属性信息
                attributes = Equipment_mapping.get('value')
                # 获取设备属性的物模型在redis中的key值
                JSON_KEY = Equipment_mapping.get('JSON_KEY')
                # print(JSON_KEY)
                pipe.get(JSON_KEY)
                # 设备物模型
                thing_model = json.loads(str(pipe.execute()[-1], encoding='utf-8'))
                properties = thing_model.get('properties')
                for attribute in attributes:
                    for pro_name, bit_seat in attribute.items():
                        # 如果是预留直接跳过
                        if pro_name == 'Reserve':
                            continue
                        # 如果一个设备属性信息信息只用一位表示
                        elif len(bit_seat) == 1:

                            for pro in properties:
                                name = pro.get('name')
                                # print(name)
                                if name == pro_name:
                                    # key = name
                                    # 获取属性名
                                    pro_name = pro.get("identifier")
                                    # print(key)
                                    unpack_content[pro_name] = int(color_code[bit_seat[0]])
                        elif len(bit_seat) > 1:
                            for pro in properties:
                                name = pro.get('name')
                                # print(name)
                                if name == pro_name:
                                    # key = name
                                    pro_name = pro.get("identifier")
                                    # print(pro_name)
                                    unpack_content[pro_name] = inversion(
                                        '0x' + color_code[bit_seat[0]:bit_seat[-1]:1])

                # pipe.xadd(name=equipment_code, fields=color_code_content, maxlen=50)

            # for i in range(len(color_code)):
            #     if color_code[i] == 1:
            #         print(i, color_code[i])
            #     print('station_pos',station_position)
                yield unpack_content
